package com.dawn.module.hospital.service.qrcodepay;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.dawn.framework.common.enums.CommonStatusEnum;
import com.dawn.framework.common.exception.ServerException;
import com.dawn.framework.common.pojo.CommonResult;
import com.dawn.framework.common.pojo.PageResult;
import com.dawn.framework.common.util.json.JsonUtils;
import com.dawn.framework.common.util.object.BeanUtils;
import com.dawn.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.dawn.module.hospital.controller.admin.qrcodepay.vo.QrcodePayPageReqVO;
import com.dawn.module.hospital.controller.admin.qrcodepay.vo.QrcodePayRefundReqVO;
import com.dawn.module.hospital.controller.admin.qrcodepay.vo.QrcodePaySaveReqVO;
import com.dawn.module.hospital.dal.dataobject.qrcodepay.QrcodePayDO;
import com.dawn.module.hospital.dal.mysql.qrcodepay.QrcodePayMapper;
import com.dawn.module.hospital.enums.BHConstants;
import com.dawn.module.hospital.enums.ErrorCodeConstants;
import com.dawn.module.hospital.enums.PayStatusEnum;
import com.dawn.module.hospital.service.CDBPayUtils;
import com.dawn.module.hospital.service.TemplateMsgService;
import com.dawn.module.hospital.util.RandomUtils;
import com.dawn.module.hospital.util.SecEncryptUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.dawn.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.dawn.module.hospital.enums.ErrorCodeConstants.MEDICAL_COPY_NOT_EXISTS;
import static com.dawn.module.hospital.enums.ErrorCodeConstants.QRCODE_PAY_NOT_EXISTS;

/**
 * 扫码支付 Service 实现类
 *
 * @author root
 */
@Service
@Validated
@Slf4j
public class QrcodePayServiceImpl implements QrcodePayService {

    @Resource
    private QrcodePayMapper qrcodePayMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private TemplateMsgService templateMsgService;

    @Override
    public Long createQrcodePay(QrcodePaySaveReqVO createReqVO) {
        // 插入
        QrcodePayDO qrcodePay = BeanUtils.toBean(createReqVO, QrcodePayDO.class);

        qrcodePay.setStatus(CommonStatusEnum.ENABLE.getStatus());
        qrcodePay.setPayStatus(PayStatusEnum.PAY_STATUS_CREATE.getCode());
        if(StringUtils.isEmpty(qrcodePay.getTitle())){
            qrcodePay.setTitle(qrcodePay.getOrderType());
        }
        qrcodePayMapper.insert(qrcodePay);
        // 返回
        return qrcodePay.getId();
    }

    @Override
    public void updateQrcodePay(QrcodePaySaveReqVO updateReqVO) {
        // 校验存在
        validateQrcodePayExists(updateReqVO.getId());
        // 更新
        QrcodePayDO updateObj = BeanUtils.toBean(updateReqVO, QrcodePayDO.class);
        qrcodePayMapper.updateById(updateObj);
    }

    @Override
    public void deleteQrcodePay(Long id) {
        // 校验存在
        validateQrcodePayExists(id);
        // 删除
        qrcodePayMapper.deleteById(id);
    }

    @Override
    public CommonResult refundQrcodePay(QrcodePayRefundReqVO reqVO) {
        // 校验存在
        QrcodePayDO qrcodePayDO = qrcodePayMapper.selectById(reqVO.getId());
        if(qrcodePayDO == null){
            throw exception(QRCODE_PAY_NOT_EXISTS);
        }

        CDBPayUtils cdbPayUtils = cdbPayUtils = CDBPayUtils.instance().build(CDBPayUtils.ABC);

        if(BHConstants.AUTH_CODE_PAY_WX.getValue().equals(qrcodePayDO.getPayModeName())){
            cdbPayUtils = CDBPayUtils.instance().build(CDBPayUtils.WX);
        } else if (BHConstants.AUTH_CODE_PAY_ALI.getValue().equals(qrcodePayDO.getPayModeName())) {
            cdbPayUtils = CDBPayUtils.instance().build(CDBPayUtils.ALI);
        } else if (BHConstants.AUTH_CODE_PAY_ABC.getValue().equals(qrcodePayDO.getPayModeName())) {
            cdbPayUtils = CDBPayUtils.instance().build(CDBPayUtils.ABC);
        } else {
            cdbPayUtils = CDBPayUtils.instance().build(CDBPayUtils.WX);
        }

        log.info("-------退款-支付订单查询 orderNumber:"+qrcodePayDO.getOrderNumber());
        CommonResult queryResult = cdbPayUtils.query(qrcodePayDO.getOrderNumber());
        log.info("-------退款-支付订单查询 payQueryResult-----json:"+ JsonUtils.toJsonString(queryResult));
        if(queryResult.isError()){
            return CommonResult.error(ErrorCodeConstants.BH_ERROR.getCode(),"退款失败,支付订单数据不存在");
        }

        JSONObject payOrder = (JSONObject)queryResult.getData();
        int refundState = payOrder.getInteger("refundState");
        if(refundState == PayStatusEnum.PAY_STATUS_PAYOFF.getCode()) {
            // 已退款
            CommonResult rqResult = cdbPayUtils.refundQuery(payOrder.getString("payOrderId"));
            if (rqResult.isSuccess()) {
                JSONArray refundArray = (JSONArray) rqResult.getData();
                JSONObject refundObject = refundArray.getJSONObject(0);

                qrcodePayDO.setPayStatus(PayStatusEnum.PAY_STATUS_REFUND.getCode());
                qrcodePayDO.setRefundNumber(refundObject.getString("mchRefundNo"));
                qrcodePayDO.setRefundTime(LocalDateTimeUtil.parse(refundObject.getString("successTime"), DatePattern.NORM_DATETIME_PATTERN));
                qrcodePayDO.setRefundPrice(qrcodePayDO.getAmount());
                qrcodePayMapper.updateById(qrcodePayDO);
                return CommonResult.success(qrcodePayDO);
            }
        }

        String refundNo = "TF" + RandomUtils.buildRandomStr(4);
        CommonResult refundResult = cdbPayUtils.refundOrder(qrcodePayDO.getOrderNumber(), refundNo, reqVO.getRefundPrice().toString(), reqVO.getRefundReason());
        if(refundResult.isSuccess()) {
            qrcodePayDO.setPayStatus(PayStatusEnum.PAY_STATUS_REFUND.getCode());
            qrcodePayDO.setRefundNumber(refundNo);
            qrcodePayDO.setRefundTime(LocalDateTime.now());
            qrcodePayDO.setRefundPrice(reqVO.getRefundPrice());
            qrcodePayMapper.updateById(qrcodePayDO);
        } else {
            return CommonResult.error(ErrorCodeConstants.BH_ERROR.getCode(),"退款失败:"+refundResult.getMsg());
        }

        return refundResult;
    }

    @Override
    public CommonResult queryQrcodePayStatus(Long id) {
        // 校验存在
        QrcodePayDO qrcodePayDO = qrcodePayMapper.selectById(id);
        if(qrcodePayDO == null){
            throw exception(QRCODE_PAY_NOT_EXISTS);
        }

        CDBPayUtils cdbPayUtils = cdbPayUtils = CDBPayUtils.instance().build(CDBPayUtils.ABC);
        if(BHConstants.AUTH_CODE_PAY_WX.getValue().equals(qrcodePayDO.getPayModeName())){
            cdbPayUtils = CDBPayUtils.instance().build(CDBPayUtils.WX);
        } else if (BHConstants.AUTH_CODE_PAY_ALI.getValue().equals(qrcodePayDO.getPayModeName())) {
            cdbPayUtils = CDBPayUtils.instance().build(CDBPayUtils.ALI);
        } else if (BHConstants.AUTH_CODE_PAY_ABC.getValue().equals(qrcodePayDO.getPayModeName())) {
            cdbPayUtils = CDBPayUtils.instance().build(CDBPayUtils.ABC);
        } else {
            cdbPayUtils = CDBPayUtils.instance().build(CDBPayUtils.WX);
        }

        CommonResult payResult =cdbPayUtils.query(qrcodePayDO.getOrderNumber());
        log.warn("-------订单查询 payQueryResult-----json:"+ JsonUtils.toJsonString(payResult));

        if(payResult.isError()){
            return payResult;
        }

        JSONObject res = (JSONObject)payResult.getData();
        Integer orderState = res.getInteger("state");
        QrcodePaySaveReqVO qrcodePayUpdateReqVO = BeanUtils.toBean(qrcodePayDO,QrcodePaySaveReqVO.class);
        qrcodePayUpdateReqVO.setPayOrderId(res.getString("payOrderId"));
        qrcodePayDO.setPayStatus(orderState);
        if(StringUtils.isNotEmpty(res.getString("channelOrderNo"))){
            qrcodePayDO.setPayTime(LocalDateTimeUtil.parse(res.getString("successTime"), DatePattern.NORM_DATETIME_PATTERN));
            qrcodePayDO.setThirdOrderNo(res.getString("channelOrderNo"));
        }

        if(orderState != PayStatusEnum.PAY_STATUS_PAYOFF.getCode()){
            qrcodePayDO.setRemark(res.getString("errMsg"));
        }

        int refundState = res.getInteger("refundState");
        if(refundState == PayStatusEnum.PAY_STATUS_PAYOFF.getCode()) {
            // 已退款
            CommonResult rqResult = cdbPayUtils.refundQuery(res.getString("payOrderId"));
            if (rqResult.isSuccess()) {
                JSONArray refundArray = (JSONArray) rqResult.getData();
                JSONObject refundObject = refundArray.getJSONObject(0);
                qrcodePayDO.setPayStatus(PayStatusEnum.PAY_STATUS_REFUND.getCode());
                qrcodePayDO.setRefundNumber(refundObject.getString("mchRefundNo"));
                qrcodePayDO.setRefundTime(LocalDateTimeUtil.parse(refundObject.getString("successTime"), DatePattern.NORM_DATETIME_PATTERN));
                qrcodePayDO.setRefundPrice(qrcodePayDO.getAmount());
            }
        }
        qrcodePayMapper.updateById(qrcodePayDO);

        return CommonResult.success(qrcodePayDO);
    }

    private void validateQrcodePayExists(Long id) {
        if (qrcodePayMapper.selectById(id) == null) {
            throw exception(QRCODE_PAY_NOT_EXISTS);
        }
    }

    @Override
    public QrcodePayDO getQrcodePay(Long id) {
        return qrcodePayMapper.selectById(id);
    }

    @Override
    public QrcodePayDO getQrcodePayByOrderNumber(String orderNumber) {
        QrcodePayDO qrcodePayDO = null;
        List<QrcodePayDO> selectList = qrcodePayMapper.selectList(QrcodePayDO::getOrderNumber,orderNumber);
        if(selectList.size()>0){
            qrcodePayDO = selectList.get(0);
        }else {
            throw exception(QRCODE_PAY_NOT_EXISTS);
        }
        return qrcodePayDO;
    }

    @Override
    public QrcodePayDO getQrcodePayByHisOrderNo(String hisOrderNo) {
        QrcodePayDO qrcodePayDO = null;
        LambdaQueryWrapperX<QrcodePayDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.eq(QrcodePayDO::getHisOrderNo,hisOrderNo)
                .or().eq(QrcodePayDO::getOrderNumber,hisOrderNo)
                .or().eq(QrcodePayDO::getPayOrderId,hisOrderNo)
                .orderByDesc(QrcodePayDO::getCreateTime);
        List<QrcodePayDO> selectList = qrcodePayMapper.selectList(queryWrapperX);
        if(selectList.size()>0){
            qrcodePayDO = selectList.get(0);
        }
        return qrcodePayDO;
    }

    @Override
    public CommonResult buildPaySignData(String orderNumber) {
        QrcodePayDO qrcodePayDO = getQrcodePayByOrderNumber(orderNumber);
        if (qrcodePayDO == null) {
            throw exception(QRCODE_PAY_NOT_EXISTS);
        }

        JSONObject object = JSONObject.parseObject(JSONObject.toJSONString(qrcodePayDO));
        object.put("orderNumber",qrcodePayDO.getOrderNumber());
        object.put("totalCost",qrcodePayDO.getAmount());

        Map<String,Object> map = new HashMap<>();
        map.putAll(object);
        JSONObject signDataJson = new JSONObject();
        signDataJson.put("patientId",qrcodePayDO.getPatientId());
        signDataJson.put("patientName",qrcodePayDO.getPatientName());
        signDataJson.put("clinicNo",qrcodePayDO.getClinicNo());
        signDataJson.put("cardNo",qrcodePayDO.getIdCardNo());
        signDataJson.put("pid",qrcodePayDO.getUserId());
        signDataJson.put("userId",qrcodePayDO.getUserId());
        signDataJson.put("openid",qrcodePayDO.getOpenid());
        signDataJson.put("registerNo",qrcodePayDO.getHisBillNos());
        signDataJson.put("orderName",qrcodePayDO.getOrderType());
        signDataJson.put("totalCost",qrcodePayDO.getAmount());
        signDataJson.put("orderNumber",orderNumber);

        stringRedisTemplate.opsForValue().set("OrderDetails_"+qrcodePayDO.getOrderNumber(),signDataJson.toJSONString(),30L, TimeUnit.MINUTES);

        String signData = null;
        try {
            signData = SecEncryptUtil.encryption(signDataJson.toJSONString());
        } catch (Exception e) {
            throw new ServerException(ErrorCodeConstants.BH_ERROR.getCode(),e.getMessage());
        }
        map.put("signDataStr", signData);
        return CommonResult.success(map);
    }

    @Override
    public CommonResult payNotify(Map params) {
        log.info("---------付款码支付成功通知-------params:"+ JSONUtil.toJsonStr(params));

        String orderNumber = String.valueOf(params.get("orderNumber"));
        String payOrderId = String.valueOf(params.get("payOrderId"));
        String thirdOrderNo = String.valueOf(params.get("thirdOrderNo"));

        QrcodePayDO qrcodePayDO = getQrcodePayByOrderNumber(orderNumber);

        if (qrcodePayDO == null) {
            throw exception(MEDICAL_COPY_NOT_EXISTS);
        }

        if (qrcodePayDO.getPayStatus() == PayStatusEnum.PAY_STATUS_PAYOFF.getCode() || qrcodePayDO.getPayStatus() == PayStatusEnum.PAY_STATUS_REFUND.getCode()) {
            return CommonResult.success("success");
        }

        qrcodePayDO.setPayOrderId(payOrderId);
        qrcodePayDO.setThirdOrderNo(thirdOrderNo);
        qrcodePayDO.setPayTime(LocalDateTime.now());
        qrcodePayDO.setPayStatus(PayStatusEnum.PAY_STATUS_PAYOFF.getCode());
        qrcodePayMapper.updateById(qrcodePayDO);

//        templateMsgService.sendMedicalPaySuccess(medicalCopyDO);
//        templateMsgService.sendMedicalPayToAdmin(medicalCopyDO);

        return CommonResult.success("success");
    }

    @Override
    public CommonResult payRefund(Map params) {
        log.info("---------退费成功通知-------params:"+ JSONUtil.toJsonStr(params));

        String orderNumber = String.valueOf(params.get("orderNumber"));
        String refundNo = String.valueOf(params.get("refundNo"));
        String refundAmount = String.valueOf(params.get("refundAmount"));
        String refundReason = String.valueOf(params.get("refundReason"));

        QrcodePayDO qrcodePayDO = getQrcodePayByOrderNumber(orderNumber);

        if (qrcodePayDO == null) {
            throw exception(MEDICAL_COPY_NOT_EXISTS);
        }

        if (qrcodePayDO.getPayStatus() == PayStatusEnum.PAY_STATUS_REFUND.getCode()) {
            return CommonResult.success("success");
        }

        qrcodePayDO.setPayStatus(PayStatusEnum.PAY_STATUS_REFUND.getCode());
        qrcodePayDO.setRefundNumber(refundNo);
        qrcodePayDO.setRefundReason(refundReason);
        qrcodePayDO.setRefundPrice(new BigDecimal(refundAmount));
        qrcodePayDO.setRefundTime(LocalDateTime.now());
        qrcodePayMapper.updateById(qrcodePayDO);

        return CommonResult.success("success");
    }

    @Override
    public CommonResult queryByOrderNumber(String orderNumber) {
        QrcodePayDO qrcodePayDO = getQrcodePayByOrderNumber(orderNumber);
        if (qrcodePayDO == null) {
            throw exception(QRCODE_PAY_NOT_EXISTS);
        }

        JSONObject object = JSONObject.parseObject(JSONObject.toJSONString(qrcodePayDO));
        if(qrcodePayDO.getPayStatus() == PayStatusEnum.PAY_STATUS_PAYOFF.getCode()){
            object.put("payStatus","success");
        }
        return CommonResult.success(object);
    }

    @Override
    public CommonResult queryPay(String orderNumber) {
        QrcodePayDO qrcodePayDO = null;
        LambdaQueryWrapperX<QrcodePayDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.eq(QrcodePayDO::getOrderNumber,orderNumber)
                .or().eq(QrcodePayDO::getHisOrderNo,orderNumber)
                .or().eq(QrcodePayDO::getPayOrderId,orderNumber)
                .orderByDesc(QrcodePayDO::getCreateTime);
        List<QrcodePayDO> selectList = qrcodePayMapper.selectList(queryWrapperX);
        if(selectList.size()>0){
            qrcodePayDO = selectList.get(0);
        }else {
            throw exception(QRCODE_PAY_NOT_EXISTS);
        }
        JSONObject object = JSONObject.parseObject(JSONObject.toJSONString(qrcodePayDO));
        return CommonResult.success(object);
    }

    @Override
    public PageResult<QrcodePayDO> getQrcodePayPage(QrcodePayPageReqVO pageReqVO) {
        return qrcodePayMapper.selectPage(pageReqVO);
    }

}